package sysu.el.graph.undi;

import sysu.el.array.algors.Sig;
import sysu.el.graph.Edge;
import sysu.el.graph.GraphConstants;

public class UndiGT implements UndiGTInterface{

	/* ******************************************************************** */
	/* ************************* Constructors ***************************** */
	/* ******************************************************************** */

	public UndiGT(int edgeNum, Edge edgeList[]){
		/** FYI: nodeNum = MAXID + 1*/
		int nodeNum = Edge.regulateEdgeList(edgeNum, edgeList) + 1;
		this.setupEnv(nodeNum);
		this.establishDataFromEdgeList(nodeNum, edgeNum, edgeList);
	}

	public UndiGT(int nodeNum, int edgeNum, Edge edgeList[]){
		{
			int tmp = Edge.regulateEdgeList(edgeNum, edgeList) + 1;
			assert(nodeNum >= tmp);
		}
		this.establishDataFromEdgeList(nodeNum, edgeNum, edgeList);
	}

	private UndiGT(){
	}

	public UndiGT(int num, int wei[][]){
		super();
		this.setupEnv(num);
		this.establishDataFromWeight(wei);
	}

	/* ******************************************************************** */
	/* *************************** Wrappers ******************************* */
	/* ******************************************************************** */
	public static UndiGT wrap(UndiGTMatrix graph){
		UndiGT gt = new UndiGT();
		gt.weight = graph;
		gt.adjNodes = new UndiGTAdjList(graph);
		return gt;
	}

	public static UndiGT wrap(UndiGTAdjList graph){
		UndiGT gt = new UndiGT();
		gt.weight = new UndiGTMatrix(graph);
		gt.adjNodes = graph;
		return gt;
	}
	
	public static UndiGT wrap(UndiGTAdjListVal graph){
		UndiGT gt = new UndiGT();
		gt.weight = new UndiGTMatrix(graph);
		gt.adjNodes = graph;
		return gt;
	}

	/* ******************************************************************** */
	/* ************************* Attribute op ***************************** */
	/* ******************************************************************** */

	// Accelerate Calculation
	/**
	 * adjNodes must be sorted in ascending order.
	 * It doesn't allows multi-edges here, but with different strategy
	 */
	public UndiGTMatrix weight;
	public UndiGTAdjList adjNodes;

	private void setupEnv(int nodeNum){
		this.weight = new UndiGTMatrix(nodeNum);
		adjNodes = new UndiGTAdjList(nodeNum);
	}

	/* ******************************************************************** */
	/* ************************* Auxiliary OP ***************************** */
	/* ******************************************************************** */

	private void establishDataFromWeight(int wei[][]){

		for (int i=0; i<this.getNodeNum(); i++){
			for (int j=i; j<this.getNodeNum(); j++){
				assert(wei[i][j] == wei[j][i]);
				this.weight.addEdge(i, j, wei[i][j]);
				if (wei[i][j] != GraphConstants.NULL_EDGE)
					this.adjNodes.addEdge(i, j);
			}
		}
	}

	private void establishDataFromEdgeList(int nodeNum, int edgeNum, Edge edgeList[]){
		int [] adjDegree= new int[nodeNum];
		for (int i=0; i<edgeNum; i++){
			int s = edgeList[i].s;
			int e = edgeList[i].e;
			weight.addEdge(s, e, edgeList[i].w);
			adjNodes.addEdge(s, e);
		}
	}

	/* ******************************************************************** */
	/* ************************* Essential OP ***************************** */
	/* ******************************************************************** */

	public String adjNodeToString(){
		//		return Sig.mkString(this.getNodeNum(), this.adjNodes, "\n", "\t");
		//		return Sig.mkString(this.getNodeNum(), this.ad)
		StringBuffer sb = new StringBuffer();
		for (int i=0; i<this.adjNodes.getNodeNum(); ++i){
			//			sb.append(this.adjNodes.getList(i));
			sb.append(Sig.mkStrBuf(this.adjNodes.getDegree(i),
					this.adjNodes.getList(i), "\t"));
			sb.append("\n");
		}
		return new String(sb);
	}

	/* ******************************************************************** */
	/* ************************* Interface OP ***************************** */
	/* ******************************************************************** */

	@Override
	public int getDegree(int node) {
		// TODO Auto-generated method stub
		return this.adjNodes.getDegree(node);
	}

	@Override
	public int[] getList(int i) {
		return adjNodes.getList(i);
	}
	
	@Override
	public int getW(int i, int j) {
		return this.weight.getW(i, j);
	}

	@Override
	public int getNodeNum() {
		return adjNodes.getNodeNum();
	}

	@Override
	public int getEdgeNum() {
		return adjNodes.getEdgeNum();
	}

	@Override
	public void addEdge(int src, int dst, int val) {
		this.weight.addEdge(src, dst, val);
		this.adjNodes.addEdge(src, dst);
	}
	
	static public UndiGT testGraph;
	static{
		int h[][] = {{0, 1, 1, 1, 1}, 
			{1, 0, 0, 0, 0}, 
			{1, 0, 0, 0, 0},
			{1, 0, 0, 0, 0}, 
			{1, 0, 0, 0, 0}};
		testGraph = new UndiGT(5, h);		
	}
}
